import os

from conf import conf
from module.static_module.parent.model import DynamicModule
import numpy as np

from tools.view import info_messagebox
from tools.framework import get_latest_file, get_ui_value, gen_result_folder_name
from core.constant import *
from core.object import BarData


class FactorAnalysisModel(DynamicModule):
    def __init__(self, master):
        super().__init__(master, Module.FactorAnalysis)
        # 实体类映射视图类变量数据
        self.data_name_ls = []
        self.research_plan_ls = []
        self.existing_factor_ls = []
        self.factor_name_ls = []
        self.result_folder_ls = []

        # 实体类结构变量
        self.data_name = None
        self.research_plan = None
        self.existing_factor = None
        self.factor_name = None
        self.result_folder = None

        self.market_factor_data_df_dc = {}
        pass

    def sec_init(self):
        # 实体类映射视图类变量数据
        self.data_name_ls = self.master.file_manager.data_def_group_dc[DataDefGroup.Mk.value]
        self.research_plan_ls = self.master.file_manager.data_def_group_dc[DataDefGroup.FSummary.value]
        self.existing_factor_ls = []
        self.factor_name_ls = self.master.file_manager.data_def_group_dc[DataDefGroup.FName.value]
        self.result_folder_ls = [gen_result_folder_name(ResultFolder.FA)]

    def get_ui_params(self):
        # 获取ui界面的相关参数
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(LabelMember.DataName)
        self.data_name = get_ui_value(values, indices, WidgetCategory.Combobox)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(LabelMember.ResearchPlan)
        self.research_plan = get_ui_value(values, indices, WidgetCategory.Combobox)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(LabelMember.ExistingFactor)
        self.existing_factor = get_ui_value(values, indices, WidgetCategory.CheckButton)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(LabelMember.FactorName)
        self.factor_name = get_ui_value(values, indices, WidgetCategory.CheckButton)
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(LabelMember.ResultFolder)
        self.result_folder = get_ui_value(values, indices, WidgetCategory.Entry)

    def on_run_thread(self):
        # try:
        self.get_ui_params()
        try:
            self.analysis()
        except Exception as e:
            import traceback
            traceback.print_exc()
        self.task_end()

        pass

    def on_only_data_thread(self):
        try:
            self.get_ui_params()
            from tools.file_manager import gen_data_name
            self.calculate_factor()
            if self.market_factor_data_df_dc is None or len(self.market_factor_data_df_dc.keys()) == 0:
                info = "未生成行情因子数据。"
                self.master.running_model.log_engine.emit(info, LogName.Running)
            else:
                # for symbol, market_factor_data in self.market_factor_data_df_dc.items():
                #     csv_name = gen_data_name(DataCategory.MkFt, symbol) + ".csv"
                #     path = os.path.join(self.master.file_manager.market_factor_data_path, csv_name)
                #     self.master.file_manager.save_csv(market_factor_data, path)
                csv_name = gen_data_name(DataCategory.MkFt) + ".csv"
                path = os.path.join(self.master.file_manager.market_factor_data_path, csv_name)
                self.master.file_manager.save_dc_csv(self.market_factor_data_df_dc, path)
        except Exception as e:
            import traceback
            info = f"任务运行出错，错误信息{e}"
            self.master.file_manager.log_engine.emit(info, LogName.Running)
            traceback.print_exc()
            pass
        # 任务结束
        self.task_end()

    def calculate_factor(self):
        from tools.framework import load_module_from_path
        self.market_factor_data_df_dc = {}
        # 若既有因子和预新增因子名称重复则报错
        for existing_factor_o in self.existing_factor:
            if existing_factor_o in self.factor_name:
                raise ValueError("既有因子和预新增因子存在重复，因子方法由于被覆盖将会报错。")

        if not self.existing_factor:
            info = "未选择既有因子。"
            self.master.running_model.log_engine.emit(info, LogName.Running)
        if not self.factor_name:
            info = "未选择预新增因子。"
            self.master.running_model.log_engine.emit(info, LogName.Running)
        if (not self.existing_factor) and (not self.factor_name):
            return
        # 计算因子值并整合至数据
        # 跟据describe_dc从数据名获取相应路径
        describe_all_dc = self.master.file_manager.describe_all_dc
        describe_dc = describe_all_dc[self.data_name]
        data_path = describe_dc.path
        # 加载因子类
        factor_am_module = load_module_from_path(self.master.file_manager.factor_am_path)

        # 加载行情数据
        market_data_df_dc = self.master.file_manager.read_dc_csv(data_path)
        for symbol, market_data_df_o in market_data_df_dc.items():
            # 从market_data_df中筛选选中的既有因子
            market_data_df = market_data_df_o[["open", "high", "low", "close", "volume"]+self.existing_factor].copy()
            info = f"开始计算{symbol}相应因子数据。"
            self.master.running_model.log_engine.emit(info, LogName.Running)
            am = factor_am_module.ArrayManagerFactor(len(market_data_df), symbol=symbol, interval=Interval.MINUTE)
            am.open_array = market_data_df['open'].to_numpy()
            am.high_array = market_data_df['high'].to_numpy()
            am.low_array = market_data_df['low'].to_numpy()
            am.close_array = market_data_df['close'].to_numpy()
            am.volume_array = market_data_df['volume'].to_numpy()
            # 整合因子
            # 遍历数据，新增因子列
            for index, f in enumerate(self.factor_name):
                method_to_call = getattr(am, f)
                # 要求所有因子给定初始值，但array参数初始值为False，为因子值自动生成提供遍历
                try:
                    result = method_to_call(array=True)
                    if result is None or len(result) != am.size:
                        info = f"因子{f}返回数组的大小与am.size不一致，将尝试逐个遍历行情以非数组形式生成因子序列。"
                        self.master.running_model.log_engine.emit(info, LogName.Running)
                        info_messagebox(info)
                        # 逐个遍历行情以非数组形式生成因子序列
                        # 获取临时am的size
                        am_size = conf.FactorAnalysis.temp_am_size.value
                        temp_am = factor_am_module.ArrayManagerFactor(am_size, symbol=symbol, interval=Interval.MINUTE)
                        temp_am.open_array = am.open_array[0:am_size]
                        temp_am.high_array = am.high_array[0:am_size]
                        temp_am.low_array = am.low_array[0:am_size]
                        temp_am.close_array = am.close_array[0:am_size]
                        temp_am.volume_array = am.volume_array[0:am_size]
                        result = np.full(am_size, np.nan)
                        # 循环更新temp_am，并开始生成factor值
                        for num, (index_o, row) in enumerate(market_data_df.iterrows()):
                            if num < am_size:
                                continue
                            symbol = symbol
                            exchange = Exchange.DDQ
                            datetime = index_o
                            interval = Interval.MINUTE
                            volume = row["volume"]
                            turnover = 0
                            open_interest = 0
                            open_price = row["open"]
                            high_price = row["high"]
                            low_price = row["low"]
                            close_price = row["close"]
                            temp_bar: BarData = BarData(symbol=symbol, exchange=exchange, datetime=datetime,
                                                        interval=interval, volume=volume, turnover=turnover,
                                                        open_interest=open_interest, open_price=open_price,
                                                        high_price=high_price, low_price=low_price,
                                                        close_price=close_price)
                            temp_am.update_bar(temp_bar)
                            method_to_call = getattr(temp_am, f)
                            result = np.append(result, method_to_call(array=False))
                            if num % 10000 == 0:
                                info = f"因子{f}相应数据已处理{num}条。"
                                self.master.running_model.log_engine.emit(info, LogName.Running)

                except Exception as e:
                    import traceback
                    traceback.print_exc()
                    info = f"仅向因子{f}传入array参数产生错误。"
                    self.master.running_model.log_engine.emit(info, LogName.Running)
                    info_messagebox(info)
                    return
                if not isinstance(result, np.ndarray):
                    raise ValueError(f"因子{f}返回值不满足一维ndarray要求。")
                # 因子值添加到数据新列
                market_data_df[f] = result
                info = f"{index+1}/{len(self.factor_name)} 因子{f}已整合。"
                self.master.running_model.log_engine.emit(info, LogName.Running)
            info = f"因子值计算完毕。"
            self.master.running_model.log_engine.emit(info, LogName.Running)
            self.market_factor_data_df_dc[symbol] = market_data_df
        pass

    def analysis(self):
        from module.logic_module.researcher.factor_summary import FactorSummary
        self.calculate_factor()
        # 导入并运行plan
        plan_ins = FactorSummary(self)
        plan_method = getattr(plan_ins, self.research_plan, None)
        if plan_method:
            plan_method()  # 具体方法结合界面确定
        else:
            info = "报告方案获取失败。"
            self.master.file_manager.log_engine.emit(info, LogName.Running)
        pass

    def on_data_name_change(self):
        values, indices = self.master.main_window_view.dynamic_module_view.auto_layout.get_value(
            LabelMember.DataName)
        self.data_name = get_ui_value(values, indices, WidgetCategory.Combobox)

        self.existing_factor_ls = self.master.file_manager.describe_all_dc[self.data_name].merge_columns_ls
        # 清除第一项，第一项特殊，为行情数据固定字段的简写
        self.existing_factor_ls.pop(0)

